﻿using System;
using System.Linq;
using System.Web.UI.WebControls;
using System.Linq.Expressions;
using System.Collections.Generic;

namespace Mvc.XForms.Utilities {
	public static class IQueryableExtensions {
		public static readonly string MethodAsc = "OrderBy";
		public static readonly string MethodDesc = "OrderByDescending";

		public static IQueryable<T> Page<T>(this IQueryable<T> query, int pageIndex, int pageSize) {
			query = query
				.Skip((pageIndex - 1) * pageSize)
				.Take(pageSize);

			return query;
		}

		/// <summary>
		/// Provides dynamic sorting based on an expression string. List accessors are currently
		/// not supported, so only simple member expressions work (eg "Address_Planet_Name").
		/// </summary>
		/// <remarks>
		/// This could easily be extended to provide list accessor support and/or multi-column
		/// sorting scenarios.
		/// </remarks>
		public static IQueryable<T> Sort<T>(this IQueryable<T> source, string sortExpression, SortDirection sortDirection) {
			if (string.IsNullOrEmpty(sortExpression))
				return source;

			var queryExpr = source.Expression;
			var propertyList = sortExpression.Split(ExpressionHelper.Separator).ToList();
			var param = Expression.Parameter(typeof(T), "item");
			var selector = BuildMemberExpression(param, propertyList);
			
			var sort = Expression.Call(
				typeof(Queryable),
				sortDirection == SortDirection.Ascending ? MethodAsc : MethodDesc,
				new Type[] { typeof(T), selector.Type },
				queryExpr,
				Expression.Quote(Expression.Lambda(selector, new ParameterExpression[] { param }))
			);

			return (IQueryable<T>)source.Provider.CreateQuery(sort);
		}

        //private static Expression BuildMemberExpression(Expression exp, IList<string> propertyList) {
        //    if (propertyList.Count == 0)
        //        return exp;

        //    var newExp = Expression.Property(exp, propertyList[0]);
        //    propertyList.RemoveAt(0);

        //    return BuildMemberExpression(newExp, propertyList);
        //}

        private static Expression BuildMemberExpression(Expression exp, IList<string> propertyList)
        {
            if (propertyList.Count == 0)
                return exp;

            List<Type> allTypes = new List<Type>();
            allTypes.Add(exp.Type);
            allTypes.AddRange(exp.Type.GetInterfaces());

            MemberExpression newExp = null;
            foreach (Type item in allTypes)
            {
                try
                {
                    newExp = Expression.Property(Expression.TypeAs(exp, item), propertyList[0]);
                    break;
                }
                catch
                {
                    continue;
                }
            }
            if (newExp == null) throw new ArgumentException();

            propertyList.RemoveAt(0);

            return BuildMemberExpression(newExp, propertyList);
        }
	}
}